#include <windows.h>

#include "lib.h"

#include "CPokerConnection.h"
#include "csgame.h"
#include "CHoldemGame.h"
#include "CDrawGame.h"
#include "log.h"
#include "stdncerr.h"
#include "CSPXMLTranslator.h"

CPokerConnection::CPokerConnection(ProgressiveServer* server, SOCKET client)
	: ProgressiveConnection(server, client)
{
	game = NULL;

	minBet = 500;	// purposefully set maxBet < minBet to disallow playing
	maxBet = 100;	// games where these values don't come from database
	compFactor = 0;

	loggedOn = false;

	m_pInfo = NULL;

    payTable = 0;
    progPayTable = 0;
	progFixedAmountMultiplier = 1;

    m_pDBInterface = NULL;

    // kharmon.  11-15-06.  Zero these out to avoid confusion later.
    msglen = 0;
    memset(msgbuf, 0, sizeof(msgbuf));

    #ifdef FORCEDSHOE
    m_pid="";
    #endif

}

CPokerConnection::~CPokerConnection()
{
	delete game;
	game = NULL;

	// CSN-7075: New Authentication
	trackLogoutSession();

    delete m_pDBInterface;
    m_pDBInterface=NULL;

	loggedOn = false;

	delete m_pInfo;
	m_pInfo = NULL;
}

void
CPokerConnection::gameDone()
{
	delete game;
	game = NULL;
}


void
CPokerConnection::setProgressiveFixedAmountMultiplier(int32 progressiveFixedAmountMultiplier)
{
	progFixedAmountMultiplier = progressiveFixedAmountMultiplier;
}



void
CPokerConnection::msgStart()
{
	msglen = 0;
}

void
CPokerConnection::msgAdd(uint8 Byte)
{
	msgbuf[msglen++] = Byte;
}

void
CPokerConnection::msgAdd(int32 SignedLong)
{
	msgbuf[msglen++] = (uint8)((SignedLong) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 8) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 16) & 0xff);
	msgbuf[msglen++] = (uint8)(((SignedLong) >> 24) & 0xff);
}

uint8*
CPokerConnection::msgGetBuf()
{
	return msgbuf + msglen;
}

void
CPokerConnection::msgReleaseBuf(int32 Amt)
{
	msglen += Amt;
}

void
CPokerConnection::msgEnd()
{
	msglen = encode(msgbuf, obuf + oused + 1, msglen);
	msglen++;	// include msg len byte
	obuf[oused] = (uint8)(msglen & 0xff);
	oused += msglen;
}

void
CPokerConnection::sendStopMsg()
{
	msgStart();
	msgAdd((uint8)'!');
	msgEnd();
}

void 
CPokerConnection::Answer( uint8 ans )
{
	msgStart();
	msgAdd(ans);
	msgEnd();
}

void 
CPokerConnection::Error( int err )
{
	msgStart();
	msgAdd((uint8)'E');
	msgAdd((uint8)(err & 0xff) );
	msgEnd();
}




int32
CPokerConnection::msgRemove(uint8* Src, int32* pResult)
{
	*pResult = (int32)(
		(Src[0] & 0xFF) |
		((Src[1] << 8) & 0xFF00) |
		((Src[2] << 16) & 0xFF0000) |
		((Src[3] << 24) & 0xFF000000));

	return 4;
}

bool8
CPokerConnection::getResponse()
{
	int32 ieat = 0, remain = iused, len;
	uint8* src = ibuf;
	int32 balance;

	// [AO 2007-10-01] We can send just one reply for certain repeating messages.
	// We're not handling partial buffers and incomplete messages but that's OK.
	char last_message = 0;

	while (remain > 0)
	{
		if (remain < src[0])
			break;

		// Handle an invalidly formatted message of length zero.
		if (src[0] == 0)
		{
			ieat += 1;
			remain -= 1;
			src += 1;
			continue;
		}

        // kharmon.  11-15-06.  Zero these out to avoid confusion later.
        msglen = 0;
        memset(msgbuf, 0, sizeof(msgbuf));

		len = decode(src + 1, msgbuf, src[0] - 1);
		ieat += src[0];
		remain -= src[0];
		src += src[0];

		switch (msgbuf[0])
		{
			case 'L':	HandlePlayerAnsiLogin	();	break;	//Login
			case 'M':	
			{
				switch (msgbuf[1])
				{
					case 'L':	HandlePlayerUnicodeLogin (); break;	//Login
					default:   
						Log::msgWarning("Connection::getResponse:  Unknown input"
							" (0x%02x)  ", msgbuf[0]);
						Answer('N');
						break;
				}
				break;
			}

			case 'D':
			{
				if (!loggedOn)
				{
					Log::msgWarning("Deal: not logged on...invalid bet  ");

					Answer('B');
					break;
				}

				if (isServerStopping())
				{
					sendStopMsg();
					break;
				}

				// [AO 2008-01-16] HA
				if (!ValidateSession())
					break;

				int32 i, bet, progressiveBet;

				i = 1;
				i += msgRemove(msgbuf + i, &bet);
				i += msgRemove(msgbuf + i, &progressiveBet);


				m_pDBInterface->trackGetBalance(&balance);

				if ( progressiveBet != 0 && progressiveBet != 100 )
                {
					Log::msgWarning("  invalid progressive bet  ");
					Answer('B');
				}
				else
				{
					if (progressiveBet == 100)
					{
						// the server is authoritative on how much the player bets.
						// we send 100 for bet or 0 for no bet
						progressiveBet *= progFixedAmountMultiplier;
					}

					if (game)
					{
						Answer('G');
					}               
				    else if (balance > 0 && (balance >= bet + progressiveBet) &&
					         bet >= minBet && bet <= maxBet)
				    {
					    int32 multiBankBet = 0;

					    // luke 3/6/02: Extended to return an error to the client on failure
					    if (progressiveBet > 0 &&
						    !placeProgressiveBet(progressiveBet, multiBankBet))
					    {
						    Log::msgWarning("  can't make progressive bet  ");

						    delete game;
						    game = NULL;

						    // Progressive error
						    Error(0x05);
						    break;
					    }

					    // luke 10/21/02: Always perform this check, even
					    // (especially?) on non-progressive bets
					    if (progressiveBet == 0 && isProgressiveOn() &&
						    !pProgServ->gameEnabled(jackpotID))
					    {
						    Error(0x05);
						    break;
					    }

                        game = this->getGame(m_eGameId, bet, progressiveBet);

					    // balance is adjusted inside of trackNewGame
					    //
					    if (game &&
						    m_pDBInterface->trackNewGame(bet, progressiveBet, multiBankBet, &balance, payTable, compFactor) &&
						    game->deal(balance))
					    {
						    m_pDBInterface->trackSaveGame(game);
					    }
					    else
					    {
						    Log::msgError("  can't create game  ");

						    delete game;
						    game = (CStudGame*)0;

						    Answer('N');
					    }
				    }
				    else if (balance > 0)	// bad bet
				    {
					    Log::msgWarning("  invalid bet  ");
					    Answer('B');
				    }
				    else					// not enough money in account
				    {
					    Log::msgWarning("  out of money  ");
					    Answer('$');
				    }
                }
				break;
			}

			case 'J':
			{
				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

				int32 jackpot, rate;
				getProgressiveData(&jackpot, &rate);
				msgStart();
				msgAdd((uint8)'J');
				msgbuf[msglen++] = (uint8)((jackpot) & 0xff);
				msgbuf[msglen++] = (uint8)(((jackpot) >> 8) & 0xff);
				msgbuf[msglen++] = (uint8)(((jackpot) >> 16) & 0xff);
				msgbuf[msglen++] = (uint8)(((jackpot) >> 24) & 0xff);
				msgbuf[msglen++] = (uint8)((rate) & 0xff);
				msgbuf[msglen++] = (uint8)(((rate) >> 8) & 0xff);
				msgbuf[msglen++] = (uint8)(((rate) >> 16) & 0xff);
				msgbuf[msglen++] = (uint8)(((rate) >> 24) & 0xff);
				msgEnd();
				break;
			}

			case 'R':	// Raise
			{
				// [AO 2008-01-16] HA
				if (!ValidateSession())
					break;

				int raiseBet = game ? (game->getBet() << 1) : 0;

				if (game)
				{
					m_pDBInterface->trackGetBalance(&balance);

					if (balance >= raiseBet)
					{
						if (!game->raise(balance, msgbuf))
						{
							Answer('N');
						}
					}
					else
					{
						Log::msgWarning("  out of money  ");
						Answer('$');
					}
				}
				else
				{
					Answer('N');
				}
				break;
			}

			case 'F':	// Fold
				// [AO 2008-01-16] HA
				if (!ValidateSession())
					break;

				if (game && m_pDBInterface)
				{
				    if( !m_pDBInterface->trackGetBalance(&balance) )
				    {
					    Answer('N');
				    }
				    else if( !(game->fold(balance)) )
				    {
					    Answer('N');
				    }
                }
				break;

            case 'm':  // Balance Request
            {
				if (isServerStopping())
	            {
		            sendStopMsg();
		            break;
	            }

				// [AO 2007-10-01] Ignore repeating messages
				// [AO 2008-01-16] HA: verify the user is still logged on
				if (msgbuf[0] == last_message || !ValidateSession())
					break;

	            //rule1: gotta be logged on
                if( loggedOn && m_pDBInterface->trackGetBalance(&balance) )
                {
					msgStart();
					msgAdd((uint8)'m');
					msgAdd((int32)balance);
					msgEnd();
                }

	            break;
            }

			default:
				Log::msgWarning("Connection::getResponse:  Unknown input"
					" (0x%02x)  ", msgbuf[0]);
				Answer('N');
				break;
		}

		last_message = msgbuf[0]; // [AO 2007-10-01] Handle repeating messages
	}

	if (iused <= ieat) // [AO 2007-10-01] Avoid buffer overflow
		iused = 0;
	else
	{
		MoveMemory(ibuf, ibuf + ieat, iused - ieat);
		iused -= ieat;
	}

	return (bool8)(oused != 0);
}

void
CPokerConnection::HandlePlayerAnsiLogin()
{
	int32 err = 0;

	if (!parseAnsiCasinoLogin(&err))
	{
		Error(err);
		return;
	}

	HandlePlayerLogin();
}

void
CPokerConnection::HandlePlayerUnicodeLogin()
{
	int32 err = 0;

	if (!parseUnicodeCasinoLogin(&err))
	{
		Error(err);
		return;
	}

	HandlePlayerLogin();
}


bool CPokerConnection::getOptions()
{
    bool bRetVal = false;
	
    bool8 progressiveOn = false; // again, make sure this isn't set by default
    uint8 progPayTable = 0;
    int32 progJackpotID = 0;
    uint8 payTable = 0;
	int32 progressiveFixAmountMultiplier = 1;


    if (m_pDBInterface->trackGetOptions(&progressiveOn, &progPayTable, &progJackpotID, 
                                        &payTable, &progressiveFixAmountMultiplier))
    {
        setProgressiveJackpotID(progJackpotID);
        setProgressiveOn(progressiveOn);
		setProgressiveFixedAmountMultiplier(progressiveFixAmountMultiplier);

        this->payTable = payTable;
        this->progPayTable = progPayTable;

        bRetVal = true;
 
    }

    return bRetVal;
}

void
CPokerConnection::HandlePlayerLogin()	//Login
{
	int32 balance;
	int32 err = 0;

	if (isServerStopping())
	{
		sendStopMsg();
		return;
	}

	if (!getOptions())
	{
		Error(err);
		return;
	}

	if (!loggedOn)
	{
		if (!m_pDBInterface->trackHasSavedGame(this, &game, &err) && err)
		{
			Error(err);
			return;
		}

		// luke 10/21/02: Finish the saved game if present;
		// otherwise, perform the following check (possibly
		// disabling the game in the process)
		if (!game && isProgressiveOn() &&
			!pProgServ->gameEnabled(jackpotID))
		{
			Log::msgInformation("Login: the game is unavailable  ");
			Error(LOGINERR_PROGERROR);
			return;
		}

		if (!m_pDBInterface->trackGetPlayerLimits(m_eGameId, &minBet, &maxBet, &compFactor))
		{
			Answer('N');
			return;
		}

		if( !m_pDBInterface->trackGetBalance(&balance) )
		{
			Answer('N');
			return;
		}


		msgStart();
		msgAdd((uint8)'L');
		msgAdd(balance);
		msgAdd(minBet);
		msgAdd(maxBet);
		msgAdd((uint8)payTable);
		msgAdd((uint8)isProgressiveOn());
		msgAdd(progPayTable);
		msgAdd((uint8)(game ? 1 : 0));
		if (game)
			game->send();
        msgAdd((int32)(progFixedAmountMultiplier * 100));
		msgEnd();

		loggedOn = true;

		pProgServ->loadJackpot(jackpotID, getForRealFlag(), getProgressiveFixedAmountMultiplier());
		int32 jackpot, rate;
		getProgressiveData(&jackpot, &rate);
		msgStart();
		msgAdd((uint8)'J');
		msgbuf[msglen++] = (uint8)((jackpot) & 0xff);
		msgbuf[msglen++] = (uint8)(((jackpot) >> 8) & 0xff);
		msgbuf[msglen++] = (uint8)(((jackpot) >> 16) & 0xff);
		msgbuf[msglen++] = (uint8)(((jackpot) >> 24) & 0xff);
		msgbuf[msglen++] = (uint8)((rate) & 0xff);
		msgbuf[msglen++] = (uint8)(((rate) >> 8) & 0xff);
		msgbuf[msglen++] = (uint8)(((rate) >> 16) & 0xff);
		msgbuf[msglen++] = (uint8)(((rate) >> 24) & 0xff);
		msgEnd();
	}
	else
	{
		Log::msgWarning("Login: attempt to re-log on  ");
		Answer('N');
	}
};


////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn     CPokerConnection::getGame(int nMachineID, int32 nBet, int32 nProgressiveBet)
///
/// Returns the appropriate type of game object for the provided machine id.
///
/// @param eMachineID       indicates which game is being played
/// @param nBet             the amount of the player's initial bet
/// @param nProgressive     the amount of the player's progressive bet
///
/// @return CGame*          returns a pointer to a new instance of the appropriate game type.
///
////////////////////////////////////////////////////////////////////////////////////////////////
CGame * CPokerConnection::getGame(CaribbeanGameId eGameID, int32 nBet, int32 nProgressiveBet)
{
    switch(eGameID)
    {
        case CARIBBEAN_STUD:
        {
            return new CStudGame(this, nBet, nProgressiveBet);
        }
        case CARIBBEAN_HOLDEM:
        {
            return new CHoldemGame(this, nBet, nProgressiveBet);
        }
        case CARIBBEAN_DRAW:        // kharmon.  09-20-07.  #16842.  Added new Caribbean Draw game.
        {
            return new CDrawGame(this, nBet, nProgressiveBet);
        }
        default:
        {
            Log::msgError("Unknown game type.");
            return NULL;
        }
    }
}

void CPokerConnection::allocateXMLTranslator( XMLTranslator** target )
{
	*target = new CSPXMLTranslator( getServer(), this );
}

// [AO 2008-01-16] Refactoring for HA
bool CPokerConnection::ValidateSession()
{
	// [AO 2008-03-26] CSN-1557: Don't check if the DB interface has not been initialized yet
	if (!m_pDBInterface)
		return true;

	int32 session_check = m_pDBInterface->trackValidateSession();
	if (!session_check) // session is valid
		return true;

	// [AO 2008-01-22] Additional case from RTG 6.6.0 code drop
	if (session_check == ProgressiveTrackSQL::NormalLogout)
		session_check = LOGINERR_NOTLOGGEDIN;

	msgStart();
	msgAdd(static_cast<uint8>('N'));
	msgAdd(static_cast<int32>(session_check));
	msgEnd();
	return false;
}
